package xyz.ibenben.zhongdian.common.util;

import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.formula.functions.T;
import org.apache.poi.ss.usermodel.*;
import xyz.ibenben.zhongdian.common.exception.ExceptionEnum;
import xyz.ibenben.zhongdian.common.exception.MyException;
import xyz.ibenben.zhongdian.system.entity.enums.*;

import java.lang.reflect.*;
import java.text.*;
import java.util.*;

/**
 * Excel工具类
 * 项目内只要牵扯到和Excel有关的，都是用此类处理问题
 * 其中有将workbook中的值放入List<Map<String, Object>>结构中、处理Map等方法
 *
 * @author chenjian
 * @since 2017年10月18日下午5:17:22
 */
public class ExcelUtils {
    private ExcelUtils(){}

    /**
     * 将workbook中的值放入List<Map<String, Object>>结构中
     * @param workbook 参数
     * @param columnNames 相应的javabean类的属性名称数组
     * @return 返回值
     */
    public static List<Map<String, Object>> parseExcel(Workbook workbook, List<String> columnNames){
        List<Map<String, Object>> result = new LinkedList<>();
        HSSFSheet sheet = (HSSFSheet) workbook.getSheetAt(0);
        for(int rowIndex = 1; rowIndex < sheet.getPhysicalNumberOfRows(); rowIndex++){   //row
            result.add(processMap(rowIndex, sheet, columnNames));
        }
        return result;
    }

    /**
     * 处理Map
     * @param rowIndex 参数
     * @param sheet 参数
     * @param columnNames 参数
     * @return 返回值
     */
    private static Map<String,Object> processMap(int rowIndex, HSSFSheet sheet, List<String> columnNames) {
        HSSFRow row = sheet.getRow(rowIndex);
        Map<String, Object> map = new HashMap<>();
        for(int cellIndex = 0; cellIndex < row.getPhysicalNumberOfCells();cellIndex++){ //cell
            HSSFCell cell = row.getCell(cellIndex);
            if(columnNames != null && !columnNames.isEmpty() && columnNames.get(cellIndex).length() > 0){
                //该列值在对应的java对象中有值
                //取出当前cell的值和对应Javabean类的属性放入到map中
                map.put(columnNames.get(cellIndex), getCellValue(cell));
            }
        }
        return map;
    }

    /**
     * 利用反射将    Map<string,object>结构 生成相应的 T 对象
     * @param list 参数
     * @param clazz 参数
     * @return 返回值
     * @throws Exception 异常
     */
    public static <T>  List<T> toObjectList(List<Map<String, Object>> list, Class<T> clazz, String format)
            throws MyException{
        List<T> returnList = new LinkedList<>();
        for(Map<String, Object> map: list){
            Set<Map.Entry<String, Object>> set =  map.entrySet();
            Iterator<Map.Entry<String, Object>> it = set.iterator();
            getMethod(clazz, it, returnList, format);
        }
        return returnList;
    }

    /**
     * 获取方法
     * @param clazz 类
     * @param it 迭代器
     * @param returnList 列表
     * @param format 格式
     * @param <T> 参数
     */
    private static <T> void getMethod(Class<T> clazz, Iterator<Map.Entry<String, Object>> it, List<T> returnList, String format) {
        try {
            T obj = clazz.newInstance();
            Method[] methods = clazz.getDeclaredMethods();
            while (it.hasNext()) {      //生成一个obj
                processMethod(methods, it, clazz, format, obj);
            }
            returnList.add(obj);
        } catch (InstantiationException e) {
            throw new MyException(ExceptionEnum.NOCONSTRUCTOREXCEPTION, e);
        } catch (IllegalAccessException e) {
            throw new MyException(ExceptionEnum.SAFEEXCEPTION, e);
        }
    }

    /**
     * 处理方法
     * @param methods 参数
     * @param it 参数
     * @param clazz 参数
     * @param format 参数
     * @param obj 参数
     * @throws IllegalAccessException 异常
     */
    private static void processMethod(Method[] methods, Iterator<Map.Entry<String, Object>> it,
                                      Class clazz, String format, Object obj) throws IllegalAccessException{
        Map.Entry<String, Object> entry = it.next();
        for(Method m: methods){
            String methodName = entry.getKey();
            StringBuilder sb = new StringBuilder(methodName);
            sb.replace(0, 1, (String.valueOf(methodName.charAt(0))).toUpperCase());
            methodName = "set" +  sb.toString();
            if(methodName.equals(m.getName()) && m.getName().startsWith("set")) {
                try {
                    processType(entry, obj, m, clazz, format);
                }catch (ParseException e) {
                    throw new MyException(ExceptionEnum.CONVERTTIMEEXCEPTION, e);
                } catch (InvocationTargetException e) {
                    throw new MyException(ExceptionEnum.JAREXCEPTION, e);
                } catch (NoSuchFieldException e) {
                    throw new MyException(ExceptionEnum.NOFILEEXCEPTION, e);
                }

                break;
            }
        }
    }

    /**
     * 处理类型
     * @param entry 参数
     * @param obj 参数
     * @param m 参数
     * @param clazz 参数
     * @param format 参数
     * @throws IllegalAccessException 异常
     * @throws InvocationTargetException 异常
     * @throws NoSuchFieldException 异常
     * @throws ParseException 异常
     */
    private static void processType(Map.Entry<String, Object> entry, Object obj, Method m, Class clazz, String format)
            throws IllegalAccessException,InvocationTargetException, NoSuchFieldException, ParseException {
        if(null == entry.getValue()){
            m.invoke(obj, entry.getValue());
            return;
        }
        Field f = clazz.getDeclaredField(entry.getKey());
        String type = f.getType().toString();//得到此属性的类型
        switch (type) {
            case "class java.lang.String":
                m.invoke(obj, entry.getValue().toString());
                break;
            case "class java.util.Date":
                SimpleDateFormat sdf = new SimpleDateFormat(format);
                m.invoke(obj, sdf.parse(entry.getValue().toString()));
                break;
            case "class java.lang.Integer":
                m.invoke(obj, new Integer(entry.getValue().toString()));
                break;
            case "class java.lang.Long":
                m.invoke(obj, new Long(entry.getValue().toString()));
                break;
            case "class java.lang.Double":
                m.invoke(obj, new Double(entry.getValue().toString()));
                break;
            case "class xyz.ibenben.zhongdian.system.entity.enums.OrientationEnum":
                m.invoke(obj, OrientationEnum.getByText(entry.getValue().toString()));
                break;
            case  "class xyz.ibenben.zhongdian.system.entity.enums.HouseTypeEnum":
                m.invoke(obj, HouseTypeEnum.getByText(entry.getValue().toString()));
                break;
            default :
                break;
        }
    }

    /**
     * 获取当前单元格内容
     * @param cell 参数
     * @return 返回值
     */
    private static Object getCellValue(Cell cell){
        Object value = null;
        if(cell != null){
            switch (cell.getCellType()) {
                case Cell.CELL_TYPE_NUMERIC:
                    value = processSwitch(cell);
                    break;
                case Cell.CELL_TYPE_STRING:
                    value = cell.getStringCellValue();
                    break;
                case Cell.CELL_TYPE_BOOLEAN:
                    value = cell.getBooleanCellValue();
                    break;
                case Cell.CELL_TYPE_ERROR:
                    break;
                case Cell.CELL_TYPE_FORMULA:
                    break;
                case Cell.CELL_TYPE_BLANK:
                    break;
                default :
                    break;
            }
        }
        return value;
    }

    /**
     * 处理Switch
     * @param cell 参数
     * @return 返回值
     */
    private static Object processSwitch(Cell cell){
        if(HSSFDateUtil.isCellDateFormatted(cell)){ //日期类型
            return HSSFDateUtil.getJavaDate(cell.getNumericCellValue());
        }else{
            return cell.getNumericCellValue();
        }
    }

}
